package com.xxx.adapter;

import com.xxx.registry.ZkUtils;
import com.xxx.request.RpcRequest;
import com.xxx.utils.IpUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import sun.jvm.hotspot.debugger.ThreadAccess;

import java.lang.reflect.Method;


/**
 * service 处理器适配器
 * 服务端需根据客户端传递过来的RpcRequest对象
 * 通过反射，动态代理技术，最终执行目标方法
 */

public class ServiceHandlerAdapter extends ChannelInboundHandlerAdapter {


    private ApplicationContext applicationContext;

    private int port;

    private long lastResponseTimeValidInterval = 5000;

    private long mockBusinessTimeMillis = 500;

    public ServiceHandlerAdapter(ApplicationContext applicationContext, int port) {
        this.applicationContext = applicationContext;
        this.port = port;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object object) throws Exception {


        long start = System.currentTimeMillis();

        RpcRequest rpcRequest = (RpcRequest) object;

        Class<?> targetClass = Class.forName(rpcRequest.getClassName());

        Object bean;
        try {
            bean = applicationContext.getBean(targetClass);
        } catch (BeansException e) {
            System.out.println("No such bean " + targetClass.getName());
            ctx.writeAndFlush("404 not found");
            return;
        }
        if (bean == null) {
            ctx.writeAndFlush("404 not found");
            return;
        }
        String targetMethodSimpleName = rpcRequest.getMethodName();

        if (targetMethodSimpleName.contains(".")) {
            targetMethodSimpleName = targetMethodSimpleName.substring(targetMethodSimpleName.lastIndexOf(".") + 1);
        }
        Method targetMethod = targetClass.getMethod(targetMethodSimpleName, rpcRequest.getParameterTypes());
        Object result = targetMethod.invoke(bean, rpcRequest.getParameters());
        ctx.writeAndFlush(result);
        Thread.sleep(mockBusinessTimeMillis);


        long end = System.currentTimeMillis();


        long period = end - start;


        try {
            recordLastResponseTime(period);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private void recordLastResponseTime(long period) throws Exception {


        System.out.println(period);
        String node = IpUtils.getAppConnectedAddress(port);
        String nodePath = "/" + node;

        CuratorFramework zkClient = ZkUtils.getZkClient();
        Stat stat = new Stat(); //0
        zkClient.getData().storingStatIn(stat).forPath(nodePath);

        int version = zkClient.setData()
                .withVersion(stat.getVersion())
                .forPath(nodePath, String.valueOf(period).getBytes()).getVersion();


        byte[] bytes = zkClient.getData().forPath(nodePath);
        String lasts = new String(bytes);

        if (lasts.length() > 0) {
            System.out.println("======The server last response time lasts " + lasts);
        } else {
            System.out.println("======The server have no last response time record");
        }


        /**
         * 监测是否5秒内为响应，如果为响应那么清空lastResponseTime
         */
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(500);

                        long lastModifyTimeMillis = stat.getMtime();

                        long currentTimeMillis = System.currentTimeMillis();

                        long updateInterval = (currentTimeMillis - lastModifyTimeMillis) / 1000;

//                        System.out.println("======Reset runable lastModifyTimeMillis "
//                                +lastModifyTimeMillis+" currentTimeMillis: "+currentTimeMillis
//                        +",updateInterval "+updateInterval);

                        //超过5秒置0
                        if (updateInterval > lastResponseTimeValidInterval) {
                            System.out.println("=========Reset last response time to 0");
                            zkClient.setData()
                                    .forPath(nodePath, "0".getBytes()).getVersion();
                        }
                    } catch (Exception e) {
//                        e.printStackTrace();
                    }
                }

            }
        });
        thread.setDaemon(true);
        thread.start();

    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }


}
